package me.sdevil507.biz.common.sms.captcha.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import me.sdevil507.biz.common.sms.SmsArgs;
import me.sdevil507.biz.common.sms.captcha.enums.CaptchaType;
import me.sdevil507.biz.common.sms.captcha.service.CaptchaService;
import me.sdevil507.sms.ali.service.AliSmsSender;
import me.sdevil507.supports.helper.Md5PasswordHelper;
import me.sdevil507.supports.helper.RandomCodeHelper;
import me.sdevil507.supports.util.IpUtil;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * SmsOperationImpl
 *
 * @author sdevil507
 */
@Service
@Slf4j
public class CaptchaServiceImpl implements CaptchaService {

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private AliSmsSender aliSmsSender;

    /**
     * 使用缓存保存验证码,映射时间戳,标记清除
     */
    private Cache<String, Long> cache;

    /**
     * 验证码过期时间(默认:5分钟,单位:s)
     */
    private long captchaExpire = 300;

    /**
     * 验证码过期清理间隔(默认:1小时,单位:s)
     */
    private long captchaCleanupInterval = captchaExpire * 12;

    @Autowired
    private Md5PasswordHelper md5PasswordHelper;

    @PostConstruct
    public void init() {
        // 获取存取验证码的业务缓存
        this.cache = cacheManager.getCache("biz-captchaCache");
        // 开启清理任务
        cleanupExpiredCaptcha();
    }

    @Override
    public boolean send(CaptchaType type, String phoneNumber) {
        // 验证码
        String captcha = RandomCodeHelper.exec(6);

        ObjectMapper mapper = new ObjectMapper();

        // 构建templateParam
        HashMap<String, String> map = new HashMap<>(1);
        map.put("code", captcha);
        String templateParam = null;
        try {
            templateParam = mapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        // 发送请求
        if (aliSmsSender.sendSms(SmsArgs.SIGN, phoneNumber, SmsArgs.T_CAPTCHA, templateParam)) {
            return save(type, phoneNumber, captcha);
        } else {
            return false;
        }
    }

    /**
     * 校验验证码
     *
     * @param type        验证码类型
     * @param phoneNumber 手机号码
     * @param code        验证码
     * @return 执行反馈
     */
    @Override
    public boolean check(CaptchaType type, String phoneNumber, String code) {
        String key = getKey(type, phoneNumber, code);
        Long timeStamp = cache.get(key);
        if (null == timeStamp) {
            // 如果验证码不存在
            return false;
        } else {
            // 如果验证码存在,判断是否过期
            //noinspection RedundantIfStatement
            if (System.currentTimeMillis() / DateUtils.MILLIS_PER_SECOND - timeStamp / DateUtils.MILLIS_PER_SECOND > captchaExpire) {
                // 如果过期了
                return false;
            } else {
                return true;
            }
        }
    }

    /**
     * 移除验证码
     *
     * @param type        验证码类型
     * @param phoneNumber 手机号码
     * @param code        验证码
     */
    @Override
    public void remove(CaptchaType type, String phoneNumber, String code) {
        String key = getKey(type, phoneNumber, code);
        cache.remove(key);
    }

    @Override
    public Boolean sendInvatiMessage( String phoneNumber,String userName,String orgName,String inviteUrl,Long inviteId) {
        ObjectMapper mapper = new ObjectMapper();
        // 构建templateParam
        HashMap<String, String> map = new HashMap<>(1);
        map.put("name", userName);
        map.put("org", orgName);
        map.put("url", inviteUrl);
        map.put("inviteId", inviteId.toString());
        String templateParam = null;
        try {
            templateParam = mapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 发送请求
        if (aliSmsSender.sendSms(SmsArgs.SIGN, phoneNumber, SmsArgs.T_INVVITE, templateParam)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Boolean sendFruitLinkMessage(String sendUserName, String sendPhoneNumber, String contactPhoneNumber, String userName, String deptName, String job, String prodType, String prodName) {
        ObjectMapper mapper = new ObjectMapper();
        // 构建templateParam
        HashMap<String, String> map = new HashMap<>(1);
        map.put("teacher", sendUserName);
        map.put("dept", deptName);
        map.put("user", userName);
//        map.put("job", job);
        map.put("type", prodType);
        map.put("name", prodName);
        map.put("phone", contactPhoneNumber);
        String templateParam = null;
        try {
            templateParam = mapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 发送请求
        if (aliSmsSender.sendSms(SmsArgs.SIGN, sendPhoneNumber, SmsArgs.T_LINKFRUIT, templateParam)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 缓存验证码
     *
     * @param type        验证码类型
     * @param phoneNumber 手机号码
     * @param code        验证码
     * @return 执行反馈
     */
    private boolean save(CaptchaType type, String phoneNumber, String code) {
        try {
            String key = getKey(type, phoneNumber, code);
            cache.put(key, System.currentTimeMillis());
            return true;
        } catch (CacheException e) {
            log.error("验证码保存失败:", e);
            return false;
        }
    }

    /**
     * 获取保存key
     *
     * @param type        验证码类型
     * @param phoneNumber 手机号
     * @param code        验证码
     * @return key
     */
    private String getKey(CaptchaType type, String phoneNumber, String code) {
        return type.name().toLowerCase() + ":" + phoneNumber + ":" + code;
    }

    /**
     * 定时清理过期的验证码,防止无限增长
     */
    private void cleanupExpiredCaptcha() {
        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("scheduled-pool-captcha-%d").daemon(true).build());
        @SuppressWarnings("Convert2Lambda")
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.info("清理过期的 captchaProperties at " + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
                Set<String> keys = cache.keys();
                for (String key : keys) {
                    if (System.currentTimeMillis() / DateUtils.MILLIS_PER_SECOND - cache.get(key) / DateUtils.MILLIS_PER_SECOND > captchaExpire) {
                        // 清除超过规定过期时间的captcha,避免无限增长
                        log.info("清理了过期captcha:" + key);
                        cache.remove(key);
                    }
                }
            }
        };
        scheduledExecutorService.scheduleAtFixedRate(runnable, captchaCleanupInterval, captchaCleanupInterval, TimeUnit.SECONDS);
    }


    @Override
    public String getImgCaptchaKey(HttpServletRequest request){
        String ipAddr = IpUtil.getIpAddr(request);
        // 请求头
        String userAgent=request.getHeader("user-Agent");
        return "user-service:captcha:" + md5PasswordHelper.md5Encrypt(ipAddr+userAgent);
    }
}
